﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RpcGenerator.AS3
{
    class AS3MessageGenerator
    {
        public static void Generate(MessageDescription description, IMessageSerializer serializer)
        {
            BeginMessage(description,serializer);

                WriteFields(description, serializer);
                WriteMessageId(description, serializer);                

                BeginReadFunction(description, serializer);
                EndReadFunction(serializer);

                BeginWriteFunction(description, serializer);
                EndWriteFunction(serializer);

            EndMessage(serializer);
        }

        private static void BeginMessage(MessageDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine(string.Format("internal class {0} implements IRpcMessage", description.Name));
            serializer.BeginBlock();
        }

        private static void EndMessage(IMessageSerializer serializer)
        {
            serializer.EndBlock();
        }

        private static void WriteMessageId(MessageDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine();
            serializer.NewLine("public function get MessageId():int");
            serializer.BeginBlock();
                serializer.NewLine(string.Format("return {0};", description.MessageId));
            serializer.EndBlock();
        }

        private static void WriteFields(MessageDescription description, IMessageSerializer serializer)
        {            
            foreach (MessageFieldDescription mfd in description.Fields)
            {
                serializer.NewLine(string.Format("public var {0}:{1};", mfd.Name,RpcGeneratorHelper.GetParameterTypeName(mfd.Type)));
            }
        }

        private static void BeginWriteFunction(MessageDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine();
            serializer.NewLine("public function Write(ns:NetStream):void");
            serializer.BeginBlock();

            WriteObjectMask(description, serializer);

            foreach (MessageFieldDescription mfd in description.Fields)
            {
                WriteField(mfd, serializer);
            }
        }

        private static void WriteObjectMask(MessageDescription description, IMessageSerializer serializer)
        {
            if (description.ObjectCount > 0)
            {
                int ObjectMaskSize = description.ObjectMaskSize == 64 ? 32 : description.ObjectMaskSize;

                int objMaskCount = description.ObjectCount / ObjectMaskSize;
                if (objMaskCount == 0)
                    objMaskCount = 1;

                for (int i = 0; i < objMaskCount; ++i)
                {
                    switch (ObjectMaskSize)
                    {
                        case 8:
                            {
                                serializer.NewLine(string.Format("var objectmask{0}:int = 0;", i));
                                break;
                            }
                        case 16:
                            {
                                serializer.NewLine(string.Format("var objectmask{0}:int = 0;", i));
                                break;
                            }
                        case 32:
                            {
                                serializer.NewLine(string.Format("var objectmask{0}:int = 0;", i));
                                break;
                            }
                    }
                }

                foreach (MessageFieldDescription mfd in description.Fields)
                {
                    if (!mfd.IsPrimitiveType())
                    {
                        if (mfd.IsString())
                        {
                            serializer.NewLine(string.Format("if({0} == null || {0}.length == 0)", mfd.Name));
                        }
                        else
                        {
                            serializer.NewLine(string.Format("if(null == {0})", mfd.Name));    
                        }                        

                        serializer.BeginBlock();
                        int maskIndex = (mfd.ObjectIndex - 1) / ObjectMaskSize;
                        int maskInnerIndex = (mfd.ObjectIndex - 1) % ObjectMaskSize;
                        serializer.NewLine(string.Format("objectmask{0} |= {1};", maskIndex, 1 << maskInnerIndex));
                        serializer.EndBlock();
                    }
                }

                for (int i = 0; i < objMaskCount; ++i)
                {
                    switch (ObjectMaskSize)
                    {
                        case 8:
                            {
                                serializer.NewLine(string.Format("ns.WriteUInt8(objectmask{0});", i));
                                break;
                            }
                        case 16:
                            {
                                serializer.NewLine(string.Format("ns.WriteUInt16(objectmask{0});", i));
                                break;
                            }
                        case 32:
                            {
                                serializer.NewLine(string.Format("ns.WriteUInt32(objectmask{0});", i));
                                break;
                            }
                    }
                }
            }
        }

        private static void WriteField(MessageFieldDescription mfd, IMessageSerializer serializer)
        {
            switch (mfd.GetMessageFieldType())
            {
                case MessageFieldType.PrimitiveType:
                    {
                        WritePrimitiveType(mfd.Type, mfd.Name, serializer);
                        break;
                    }
                case MessageFieldType.GenericType:
                    {
                        serializer.NewLine(string.Format("if({0}!=null)", mfd.Name));
                        serializer.BeginBlock();
                        serializer.NewLine(string.Format("var count:int = {0}.Count;", mfd.Name));
                        WritePrimitiveType(typeof(Int32), "count", serializer);
                        serializer.NewLine("for(var i:int = 0;i<count;++i)");
                        serializer.BeginBlock();
                        Type genericType = mfd.GetGenericType();
                        switch (MessageFieldDescription.GetMessageFieldType(genericType))
                        {
                            case MessageFieldType.PrimitiveType:
                                {
                                    WritePrimitiveType(genericType, string.Format("{0}[i]", mfd.Name), serializer);
                                    break;
                                }
                            case MessageFieldType.ClientType:
                                {
                                    serializer.NewLine(string.Format("{0}[i].Write(ns);", mfd.Name));
                                    break;
                                }
                            case MessageFieldType.GenericType:
                                {
                                    serializer.NewLine("模板中不支持模板");
                                    break;
                                }
                        }
                        serializer.EndBlock();
                        serializer.EndBlock();
                        break;
                    }
                case MessageFieldType.ClientType:
                    {
                        serializer.NewLine(string.Format("if(null != {0})", mfd.Name));
                            serializer.BeginBlock();
                            serializer.NewLine(string.Format("{0}.Write(ns);", mfd.Name));
                        serializer.EndBlock();
                        break;
                    }
            }
        }

        private static void WritePrimitiveType(Type tp, string name, IMessageSerializer serializer)
        {
            if (tp == typeof(sbyte))
            {
                serializer.NewLine(string.Format("ns.WriteInt8({0});", name));
                return;
            }

            if (tp == typeof(byte))
            {
                serializer.NewLine(string.Format("ns.WriteUInt8({0});", name));
                return;
            }

            if (tp == typeof(Int16))
            {
                serializer.NewLine(string.Format("ns.WriteInt16({0});", name));
                return;
            }

            if (tp == typeof(UInt16))
            {
                serializer.NewLine(string.Format("ns.WriteUInt16({0});", name));
                return;
            }

            if (tp == typeof(Int32))
            {
                serializer.NewLine(string.Format("ns.WriteInt32({0});", name));
                return;
            }

            if (tp == typeof(UInt32))
            {
                serializer.NewLine(string.Format("ns.WriteUInt32({0});", name));
                return;
            }

            if (tp == typeof(Int64))
            {
                serializer.NewLine(string.Format("ns.WriteInt64({0});", name));
                return;
            }

            if (tp == typeof(UInt64))
            {
                serializer.NewLine(string.Format("ns.WriteUInt64({0});", name));
                return;
            }

            if (tp == typeof(float))
            {
                serializer.NewLine(string.Format("ns.WriteFloat({0});", name));
                return;
            }

            if (tp == typeof(double))
            {
                serializer.NewLine(string.Format("ns.WriteFloat({0});", name));
                return;
            }

            if (tp == typeof(bool))
            {
                serializer.NewLine(string.Format("ns.WriteBool({0});", name));
                return;
            }

            if (tp.IsEnum)
            {
                serializer.NewLine(string.Format("ns.WriteInt32({0});", name));
                return;
            }
        }

        private static void EndWriteFunction(IMessageSerializer serializer)
        {
            serializer.EndBlock();
        }

        private static void BeginReadFunction(MessageDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine();
            serializer.NewLine("public function Read(ns:NetStream):void ");
            serializer.BeginBlock();

            ReadObjectMask(description, serializer);

            foreach (MessageFieldDescription mfd in description.Fields)
            {
                ReadField(mfd, serializer,description);
            }
        }

        private static void ReadObjectMask(MessageDescription description, IMessageSerializer serializer)
        {
            if (description.ObjectCount > 0)
            {
                int ObjectMaskSize = description.ObjectMaskSize == 64 ? 32 : description.ObjectMaskSize;
                int objMaskCount = description.ObjectCount / ObjectMaskSize;
                if (objMaskCount == 0)
                    objMaskCount = 1;

                for (int i = 0; i < objMaskCount; ++i)
                {
                    switch (ObjectMaskSize)
                    {
                        case 8:
                            {
                                serializer.NewLine(string.Format("var objectmask{0}:int = ns.ReadUInt8();", i));
                                break;
                            }
                        case 16:
                            {
                                serializer.NewLine(string.Format("var objectmask{0}:int = ns.ReadUInt16();", i));
                                break;
                            }
                        case 32:
                            {
                                serializer.NewLine(string.Format("var objectmask{0}:int = ns.ReadUInt32();", i));
                                break;
                            }
                    }
                }
            }
        }

        private static void ReadField(MessageFieldDescription mfd, IMessageSerializer serializer, MessageDescription description)
        {
            int ObjectMaskSize = description.ObjectMaskSize == 64 ? 32 : description.ObjectMaskSize;
            switch(mfd.GetMessageFieldType())
            {
                case MessageFieldType.PrimitiveType:
                    {
                        ReadPrimitiveType(mfd.Type, mfd.Name, serializer);
                        break;
                    }
                case MessageFieldType.GenericType:
                    {
                        int maskIndex = (mfd.ObjectIndex - 1) / ObjectMaskSize;
                        int maskInnerIndex = (mfd.ObjectIndex - 1) % ObjectMaskSize;
                        serializer.NewLine(string.Format("if((objectmask{0}&{1})==0)", maskIndex, 1 << maskInnerIndex));
                        serializer.BeginBlock();
                            serializer.NewLine(string.Format("{0} = new Array();", mfd.Name));
                            serializer.NewLine("var count:int = ns.ReadInt32();");
                            serializer.NewLine("for(var i:int = 0;i<count;++i)");
                            serializer.BeginBlock();
                            Type genericType = mfd.GetGenericType();
                            switch (MessageFieldDescription.GetMessageFieldType(genericType))
                            {
                                case MessageFieldType.PrimitiveType:
                                    {
                                        serializer.NewLine(string.Format("var member:{0};", genericType.Name));
                                        ReadPrimitiveType(genericType, "member", serializer);
                                        serializer.NewLine(string.Format("{0}.push(member);", mfd.Name));
                                        break;
                                    }
                                case MessageFieldType.ClientType:
                                    {
                                        serializer.NewLine(string.Format("var member:{0} = new {0}();", genericType.Name));
                                        serializer.NewLine("member.Read(ns);");
                                        serializer.NewLine(string.Format("{0}.push(member);", mfd.Name));
                                        break;
                                    }
                                case MessageFieldType.GenericType:
                                    {
                                        serializer.NewLine("模板中不支持模板");
                                        break;
                                    }
                            }
                            serializer.EndBlock();
                        serializer.EndBlock();
                        break;
                    }
                case MessageFieldType.ClientType:
                    {
                        int maskIndex = (mfd.ObjectIndex - 1) / ObjectMaskSize;
                        int maskInnerIndex = (mfd.ObjectIndex - 1) % ObjectMaskSize;
                        serializer.NewLine(string.Format("if((objectmask{0}&{1})==0)", maskIndex, 1 << maskInnerIndex));
                        serializer.BeginBlock();
                            serializer.NewLine(string.Format("{0} = new {1}();", mfd.Name, mfd.Type.Name));
                            serializer.NewLine(string.Format("{0}.Read(ns);", mfd.Name));
                        serializer.EndBlock();
                        break;
                    }
            }
        }

        private static void ReadPrimitiveType(Type tp, string name, IMessageSerializer serializer)
        {
            if (tp == typeof(sbyte))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt8();", name));
                return;
            }

            if (tp == typeof(byte))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt8();", name));
                return;
            }

            if (tp == typeof(Int16))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt16();", name));
                return;
            }

            if (tp == typeof(UInt16))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt16();", name));
                return;
            }

            if (tp == typeof(Int32))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt32();", name));
                return;
            }

            if (tp == typeof(UInt32))
            {
                serializer.NewLine(string.Format("{0} = ns.WriteUInt32();", name));
                return;
            }

            if (tp == typeof(Int64))
            {
                serializer.NewLine(string.Format("{0} = ns.WriteInt64();", name));
                return;
            }

            if (tp == typeof(UInt64))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt64();", name));
                return;
            }

            if (tp == typeof(float))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadFloat();", name));
                return;
            }

            if (tp == typeof(double))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadFloat();", name));
                return;
            }

            if (tp == typeof(bool))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadBool();", name));
                return;
            }

            if (tp.IsEnum)
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt32();", name));
                return;
            }
        }

        private static void EndReadFunction(IMessageSerializer serializer)
        {
            serializer.EndBlock();
        }
    }
}
